package finalgame.GUI.stage;

import java.io.File;
import java.util.LinkedList;

import org.lwjgl.input.Keyboard;
import org.newdawn.slick.opengl.Texture;

import finalgame.Game;
import finalgame.Engine.Battle;
import finalgame.Engine.GeneratePokemon;
import finalgame.Engine.pokemon.Pokemon;
import finalgame.Engine.trainer.Trainer;
import finalgame.Graphics.GraphicsConstants;
import finalgame.Graphics.Location;
import finalgame.Graphics.Render2D;
import finalgame.Graphics.RenderObj;
import finalgame.GUI.Character;
import finalgame.GUI.TransitoryStage;
import finalgame.GUI.battle.BattleGrass;
import finalgame.GUI.battle.BattleScreen;
import finalgame.GUI.battle.BattleScreenManager;

public class Stage implements RenderObj {
	public static final Location defaultDrawLoc = new Location(250, 220);
	public static final Location defaultWindow = new Location(400, 400);

	protected double windowWidth, windowHeight;
	protected Texture bg;
	protected Location vLoc, dLoc, windowCenter, vWindowCenter, spawn;
	protected double winw, winh; // Width of the window in terms of the texture
	protected double scale;
	protected double insetx, insety;
	int min_border = 20;
	protected LinkedList<Element> objects;
	protected LinkedList<Character> trainers;
	protected boolean wildStage = true;

	public Stage(Texture tex) {
		this((int) defaultWindow.x(), (int) defaultWindow.y(),
				(int) defaultDrawLoc.x(), (int) defaultDrawLoc.y(), tex);
	}

	public Stage(int w, int h, Texture tex) {
		this(w, h, (int) defaultDrawLoc.x(), (int) defaultDrawLoc.y(), tex);
	}

	public Stage(int w, int h, int x, int y, Texture tex) {
		windowWidth = w;
		windowHeight = h;
		dLoc = new Location(x, y);
		vLoc = new Location(0, 0);
		vWindowCenter = new Location(0, 0);
		bg = tex;
		scale = GraphicsConstants.SCALE;
		if (windowWidth > tex.getImageWidth() * scale)
			windowWidth = bg.getImageWidth();
		if (windowHeight > tex.getImageHeight() * scale)
			windowHeight = bg.getImageHeight();
		setScale(GraphicsConstants.SCALE);
		setLoc(0, 0);
		objects = new LinkedList<Element>();
		trainers = new LinkedList<Character>();
	}

	public void setScale(double scale) {
		if (scale <= 0)
			return;
		this.scale = scale;
		winw = windowWidth * bg.getWidth() / (bg.getImageWidth() * scale);
		winh = windowHeight * bg.getHeight() / (bg.getImageHeight() * scale);
		insety = windowHeight * bg.getHeight() / (2 * scale);
		insetx = windowWidth * bg.getWidth() / (2 * scale);
	}

	public void setScaleAndResize(double scale) {
		if (scale <= 0)
			return;
		this.scale = scale;
		windowWidth *= scale;
		windowHeight *= scale;
		winw = windowWidth * bg.getWidth() / (bg.getImageWidth() * scale);
		winh = windowHeight * bg.getHeight() / (bg.getImageHeight() * scale);
		insety = windowHeight * bg.getHeight() / (2 * scale);
		insetx = windowWidth * bg.getWidth() / (2 * scale);
	}

	public void resize(int w, int h) {
		windowWidth = w;
		windowHeight = h;
		setScale(scale);
	}
	
	public void setLoc(double nx, double ny) {
		windowCenter = bound(nx, ny);
		
		if (nx < insetx * scale)
			nx = insetx * scale;
		else if (nx > (bg.getImageWidth() * bg.getWidth() - insetx) * scale)
			nx = (bg.getImageWidth() * bg.getWidth() - insetx) * scale;
		if (ny < insety * scale)
			ny = insety * scale;
		else if (ny > (bg.getImageHeight() * bg.getHeight() - insety) * scale)
			ny = (bg.getImageHeight() * bg.getHeight() - insety) * scale;
		vWindowCenter.x(nx);
		vWindowCenter.y(ny);
		vLoc.x(nx / (bg.getImageWidth() * scale));
		vLoc.y(ny / (bg.getImageHeight() * scale));
	}

	public void translate(double dx, double dy) {
		setLoc(windowCenter.x() + dx * bg.getWidth(), windowCenter.y() + dy * bg.getHeight());
	}
	
	protected int bound(double lower, double upper, double num) {
		num = lower > num ? lower : num;
		num = upper < num ? upper : num;
		return (int) num;
	}
	
	protected boolean bounded(double lower, double upper, double num) {
		return (lower <= num) && (num <= upper);
	}
	
	public Location bound(Location loc) {
		return bound(loc.x(), loc.y());
	}
	
	public Location bound(double nx, double ny) {
		if (nx < min_border * scale)
			nx = min_border * scale;
		else if (nx > (bg.getImageWidth() * bg.getWidth() - min_border) * scale)
    		nx = (bg.getImageWidth() * bg.getWidth() - min_border) * scale;

		if (ny < min_border * scale)
    		ny = min_border * scale;
		else if (ny > (bg.getImageHeight() * bg.getHeight() - min_border) * scale)
			ny = (bg.getImageHeight() * bg.getHeight() - min_border) * scale;
		return new Location(nx, ny);
	}
	
	public Location getPRenderLoc(Location pLoc) {
		Location delta = new Location(pLoc.x() - vWindowCenter.x(), pLoc.y()
				- vWindowCenter.y());
		delta.x(delta.x() / (bg.getWidth()) + dLoc.x());
		delta.y(delta.y() / (bg.getHeight()) + dLoc.y());
		if(!bounded(-windowWidth / 2 + dLoc.x(), windowWidth / 2 + dLoc.x(), delta.x())) return null;
		if(!bounded(-windowHeight / 2 + dLoc.y(), windowHeight / 2 + dLoc.y(), delta.y())) return null;
		return delta;
	}

	public void render() {
		Render2D.drawRect(dLoc, (int) windowWidth + 8, (int) windowHeight + 8, new float[] { 0, 0, 0 }, true);
		Render2D.drawTex(new double[] { dLoc.x(), dLoc.y() }, new double[] {
				windowWidth, windowHeight }, 0, new double[] { windowWidth / 2,
				windowHeight / 2 }, new double[] { vLoc.x(), vLoc.y() },
				new double[] { winw, winh }, bg);
		for(Character c : trainers) c.render();
		Game.game.hero.render();
	}
	
	private double getPercentError(double a, double b) {
		return Math.abs((a - b) / b);
	}
	
	public Character interact(Character me) {
		Location myLoc = me.vLoc;
		double meFacing = me.getDirection().getAngle();
		for(Character other : trainers) {
			Location otherLoc = other.vLoc;
			double angle = Math.atan2(otherLoc.y() - myLoc.y(), myLoc.x() - otherLoc.x()) * 180 / Math.PI;
			double dist = Math.sqrt(
				Math.pow(myLoc.x() - otherLoc.x(), 2) +
				Math.pow(myLoc.y() - otherLoc.y(), 2)
			);
			double otherFacing = other.getDirection().getDir() * 90;
			if(getPercentError(meFacing, angle) < 0.2 && dist < 25) {
				if(me.getDirection().facing(other.getDirection())) {
					return other;
				}
			}
		}
		return null;
	}
	
	public void update() {
		Game.game.hero.update();
		if(Game.game.hero.moved && wildStage) {
			double rand = Math.random();
			if(rand < 0.01) {
				Pokemon p = GeneratePokemon.generatePokemon(Game.game.hero.getTrainer());
				Battle battle = new Battle(Game.game.hero.getTrainer(), p);
				BattleScreen scene = (BattleScreen) BattleScreenManager.getNewBattleScene("battlegrass");
				scene.use(battle);
				Game.delve(scene);
				Game.delve(new TransitoryStage("Random Battle!!!"));
				Game.say("Wild " + p.getName() + " attacked.", "Alert");
			}
		}
	}

	public void reset() {
		setLoc(spawn.x(), spawn.y());
	}
	
	public boolean collisionDetect(Element e) {
		if(Game.noclip) return false;
		for(Element other : objects) {
			if(e.intersects(other)) return true;
		}
		for(Character c : trainers) {
			if(c.intersects(e)) return true;
		}
		return false;
	}
	
	public double getWScale() {
		return bg.getWidth();
	}
	
	public double getHScale() {
		return bg.getHeight();
	}
	
	public void scaleUp(Location loc) {
		loc.x(loc.x() * getWScale());
		loc.y(loc.y() * getHScale());
	}
	
	public void addTrainer(String type, String name, Location loc) {
		Character c = new Character(name);
		Trainer t = null;
		boolean uninstantiated = true;
		try {
			Class clazz = Game.class.getClassLoader().loadClass("finalgame.Engine.trainer.trainer" + type);
			t = (Trainer) clazz.newInstance();
			uninstantiated = false;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		if(uninstantiated) {
			t = new Trainer();
		}
		t.setName(name);
		scaleUp(loc);
		t.setLoc(loc);
		c.setTrainer(t);
		c.setStage(this);
		c.setLocation(loc);
		c.updateBounds();
		trainers.add(c);
	}
}
